package com.ruoyi.web.controller.backstage;


import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.ChunkServletUtils;
import com.ruoyi.system.domain.IFileChunkInfo;
import com.ruoyi.system.domain.IFileEntireInfo;
import com.ruoyi.system.domain.UploadResult;
import com.ruoyi.system.domain.vo.IFileInfoVo;
import com.ruoyi.system.service.IFileChunkInfoService;
import com.ruoyi.system.service.IFileEntireInfoService;
import com.ruoyi.common.utils.file.SftpUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

/**
 * @author Administrator
 */
@RestController
@RequestMapping("/uploader")
public class UploaderController extends BaseController {

    @Value("${ruoyi.profile}")
    private String uploadFolder;

    /**
     * ip
     */
    @Value("${sftp.ftpIp}")
    private String ftpIp;
    /**
     *  端口
     */
    @Value("${sftp.port}")
    private Integer PORT;

    /**
     * 用户名
     */
    @Value("${sftp.username}")
    private String USERNAME;

    /**
     * 密码
     */
    @Value("${sftp.password}")
    private String PASSWORD;

    @Value("${sftp.fileIP}")
    public String fileAddress;

    @Value("${interfaceAddress.portalAddress}")
    public String portalAddress;

    @Resource
    private IFileEntireInfoService fileInfoService;

    @Resource
    private IFileChunkInfoService chunkService;

    /**
     * 上传文件块
     * @param chunk
     * @return
     */
    @PostMapping("/chunk")
    public String uploadChunk(IFileChunkInfo chunk) {
        logger.info("开始分片上传");
        String apiRlt = "200";
        logger.info("查询是否已分片上传 --> 入参 , chunk :  {},{}",chunk.getFilename(),chunk.getChunkNumber());
        MultipartFile file = chunk.getUpfile();
        try {
            byte[] bytes = file.getBytes();
            InputStream inputStream =(InputStream) new ByteArrayInputStream(bytes);
            SftpUtil sftpUtil = new SftpUtil(ftpIp,PORT,USERNAME,PASSWORD);
            String newFolder = uploadFolder + "/" + chunk.getIdentifier();
            String newChunkFileName = chunk.getFilename() + "-" + chunk.getChunkNumber();
            sftpUtil.put(inputStream, newFolder,newChunkFileName);
            if(chunkService.insertAsyncChunkInfo(chunk) < 0){
                apiRlt = "415";
            }
        } catch (Exception e) {
            e.printStackTrace();
            apiRlt = "415";
        }
        return apiRlt;
    }

    @GetMapping("/chunk")
    public UploadResult checkChunk(IFileChunkInfo chunk, HttpServletResponse response) {
        logger.info("开始查询是否已分片上传");
        logger.info("查询是否已分片上传 --> 入参 , chunk :  {},{}",chunk.getFilename(),chunk.getChunkNumber());
        UploadResult ur = new UploadResult();
        //默认返回其他状态码，前端不进去checkChunkUploadedByResponse函数，正常走标准上传
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

        String file = uploadFolder + "/" + chunk.getIdentifier() + "/" + chunk.getFilename();
        String folder = uploadFolder + "/" + chunk.getIdentifier();
        System.out.println("分片上传2 文件地址：" + file);
        //先判断整个文件是否已经上传过了，如果是，则告诉前端跳过上传，实现秒传
        SftpUtil sftpUtil = new SftpUtil(ftpIp, PORT, USERNAME, PASSWORD);
        if (sftpUtil.fileExist(file)) {
            sftpUtil.delete(folder,chunk.getFilename());
            IFileEntireInfo fileInfo = new IFileEntireInfo();
            fileInfo.setFilename(chunk.getFilename());
            fileInfo.setIdentifier(chunk.getIdentifier());
            fileInfoService.deleteAsyncFileInfoByFile(fileInfo);
            logger.info("文件已存在,删除重新上传,文件名称： {}",fileInfo.getFilename());
            return ur;
        }

        //如果完整文件不存在，则去数据库判断当前哪些文件块已经上传过了，把结果告诉前端，跳过这些文件块的上传，实现断点续传
        List<Integer> list = chunkService.selectAsyncChunkNumList(chunk);
        if (list !=null && list.size() > 0) {
            ur.setSkipUpload(false);
            ur.setUploadedChunks(list);
            response.setStatus(HttpServletResponse.SC_OK);
            ur.setMessage("部分文件块已存在，继续上传剩余文件块，实现断点续传");
            return ur;
        }
        return ur;
    }

    @PostMapping("/mergeFile")
    public AjaxResult mergeFile(@RequestBody IFileInfoVo fileInfoVO){
        logger.info("开始执行合并操作");
        String rlt = "FALURE";

        //前端组件参数转换为model对象
        IFileEntireInfo fileInfo = new IFileEntireInfo();
        fileInfo.setFilename(fileInfoVO.getName());
        fileInfo.setIdentifier(fileInfoVO.getUniqueIdentifier());
        fileInfo.setId(fileInfoVO.getId());
        fileInfo.setTotalSize(fileInfoVO.getSize());
        fileInfo.setRefProjectId(fileInfoVO.getRefProjectId());

        //进行文件的合并操作
        String filename = fileInfo.getFilename();
        String file = uploadFolder + "/" + fileInfo.getIdentifier() + "/" + filename;
        String folder = uploadFolder + "/" + fileInfo.getIdentifier();
        SftpUtil sftpUtil = new SftpUtil(ftpIp,PORT,USERNAME,PASSWORD);
        String fileSuccess = null;
        try {
            fileSuccess = sftpUtil.merge(file, folder, filename);
            logger.info("合并完文件返回码,{}",fileSuccess);
        } catch (Exception e) {
            e.printStackTrace();
        }
        fileInfo.setLocation(file);
        fileInfo.setFileAddr(fileAddress+"/profile/"+fileInfo.getIdentifier()+"/"+fileInfo.getFilename());
        fileInfo.setFileUrl(portalAddress+"/uploader/download/"+fileInfo.getIdentifier());
        //文件合并成功后，保存记录至数据库
        if("200".equals(fileSuccess)) {
            if(fileInfoService.insertAsyncFileInfo(fileInfo) > 0){
                IFileChunkInfo iFileChunkInfo = new IFileChunkInfo();
                iFileChunkInfo.setFilename(fileInfo.getFilename());
                iFileChunkInfo.setIdentifier(fileInfo.getIdentifier());
                chunkService.deleteAsyncChunkInfoByFile(iFileChunkInfo);
                logger.info("文件合并完成,删除分片信息,文件名称： {}",fileInfo.getFilename());
                rlt = "SUCCESS";
            }
        }

        //如果已经存在，则判断是否同一个项目，同一个项目的不用新增记录，否则新增
        if("300".equals(fileSuccess)) {
            List<IFileEntireInfo> tfList = fileInfoService.selectAsyncFileInfoList(fileInfo);
            if(tfList != null) {
                if(tfList.size() == 0 || (tfList.size() > 0 && !fileInfo.getRefProjectId().equals(tfList.get(0).getRefProjectId()))) {
                    if(fileInfoService.insertAsyncFileInfo(fileInfo) > 0){
                        rlt = "SUCCESS";
                    }
                }
            }
        }

        return AjaxResult.success(rlt);
    }

    /**
     * 查询列表
     *
     * @return ApiResult
     */
    @RequestMapping(value = "/selectFileList", method = RequestMethod.POST)
    public TableDataInfo selectFileList(@RequestBody IFileEntireInfo iFileEntireInfo){
        startPage();
        List<IFileEntireInfo> list =  fileInfoService.selectAsyncFileInfoList(iFileEntireInfo);
        return getDataTable(list);
    }

    @RequestMapping(value = "/selectFileUrl", method = RequestMethod.POST)
    public AjaxResult selectFile(@RequestBody IFileInfoVo fileInfoVO){
        logger.info("开始查询分片上传文件信息");
        IFileEntireInfo fileInfo = new IFileEntireInfo();
        fileInfo.setFilename(fileInfoVO.getName());
        fileInfo.setIdentifier(fileInfoVO.getUniqueIdentifier());
        fileInfo.setId(fileInfoVO.getId());
        fileInfo.setTotalSize(fileInfoVO.getSize());
        fileInfo.setRefProjectId(fileInfoVO.getRefProjectId());
        IFileEntireInfo iFileEntireInfo =  fileInfoService.selectAsyncFileInfoByOne(fileInfo);
        iFileEntireInfo.setUploadUrl(fileAddress+"/profile/"+iFileEntireInfo.getIdentifier()+"/"+iFileEntireInfo.getFilename());
        logger.info("查询分片上传文件信息,地址：{}",iFileEntireInfo.getUploadUrl());
        return AjaxResult.success(iFileEntireInfo);
    }

    /**
     * 下载文件
     * @param req
     * @param resp
     */
    @RequestMapping(value = "/download/{identifier}", method = RequestMethod.GET)
    public void download(HttpServletRequest req, HttpServletResponse resp ,@PathVariable("identifier") String identifier){
        logger.info("开始下载：入参 {}",identifier);
        IFileEntireInfo iFileEntireInfo =  fileInfoService.selectAsyncFileInfoByIdentifier(identifier);
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        OutputStream fos = null;
        SftpUtil sftpUtil = new SftpUtil(ftpIp, PORT, USERNAME, PASSWORD);
        InputStream download = null;

        try {
            download = sftpUtil.download(uploadFolder+"/"+identifier, iFileEntireInfo.getFilename());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            bis = new BufferedInputStream(download);
            fos = resp.getOutputStream();
            bos = new BufferedOutputStream(fos);
            ChunkServletUtils.setFileDownloadHeader(req, resp, iFileEntireInfo.getFilename());
            int byteRead = 0;
            byte[] buffer = new byte[8192];
            while ((byteRead = bis.read(buffer, 0, 8192)) != -1) {
                bos.write(buffer, 0, byteRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bos.flush();
                bis.close();
                fos.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 删除
     */
    @RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
    public AjaxResult deleteFile(@RequestBody IFileEntireInfo tFileInfo ){
        int result = fileInfoService.deleteAsyncFileInfoById(tFileInfo.getId());
        return AjaxResult.success(result);
    }



    public String generatePath(String uploadFolder, IFileChunkInfo chunk) {
        StringBuilder sb = new StringBuilder();
        sb.append(uploadFolder).append("/").append(chunk.getIdentifier());
        //判断uploadFolder/identifier 路径是否存在，不存在则创建
        if (!Files.isWritable(Paths.get(sb.toString()))) {
            logger.info("path not exist,create path: {}", sb.toString());
            try {
                Files.createDirectories(Paths.get(sb.toString()));
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }

        return sb.append("/")
                .append(chunk.getFilename())
                .append("-")
                .append(chunk.getChunkNumber()).toString();
    }

}
